執行環境就是當前Javascript代碼被執行時所在的環境,Javascript在運行任何代碼都是在執行環境運行,在執行環境的創建階段以下三個都會被建立:
全域執行環境 : 不在任何函數內的代碼,全都位於全域執行環境。他做了兩件事,一是創建全域對象,在瀏覽器中就是window對象,二是將this指向這個全局對象,一個程序中,只能存在一個全域執行環境。
函數執行環境 : 每次呼叫函數時,都會為該函數創建一個新的執行環境。每個函數都有獨立的執行環境,但是只有函數被調用時才會創建。
創建階段 -> 執行階段 -> 回收階段
執行階段
創建後,就開始執行代碼,會完成賦值(創建階段有創建變數了)
回收階段
函數調用完畢後,等待垃圾回收器回收執行環境。
用一個例子來說明:
let a = 10
function out() {
let b = 20;
function inner() {
let c = 30;
console.log(a+b+c);
}
inner();
}
out();
上面的代碼會歷經底下過程:
其中每個執行環境都是依序進入call stack中,執行完畢回收後就會離開call stack。
變數對象(VO)是一個類似容器的對象,與作用域、作用域鏈息息相關。
關於第三點舉個例子:
function test() {
function inner() {
console.log("inner");
}
var inner = 123
console.log(inner)
}
test() // 123
按照第三點的規則,inner屬性名稱已經聲明過了,所以底下var所聲明的是會跳過的,那為什麼執行結果最後還是123呢?因為這三條規則只有在變數對象的創建時適用,而賦值123是發生在執行階段,所以結果自然是123,這種現象很讓人頭疼,其實也是因為var聲明的變數允許重複命名所導致的,若使用let來聲明就可以避免這種狀況(上面的例子改為let,則會出現'func' has already been declared)。
執行環境還沒進入執行階段時,變數對象中的屬性都不能被訪問。但是進入執行階段,激活為活動對象就可以被訪問,然後開始執行執行階段的操作。
定義 : 多個執行環境(作用域)的變數對象串聯起來組成的鏈表就是作用域鏈。
作用域可以比擬為蒸籠,最底下一層為全局AO,裡面的蒸汽(變數與函數的可見性),可以滲透整個蒸籠,底層之上的其他蒸籠層的蒸氣只能往上,也就是只能影響上面的蒸籠層。
作用域的頂端一定是當前作用域(local scope)對應的變數對象,底端一定是全局作用域對應的變數對象(全局VO)。
根據以上的了解,查找變數與函數時JS引擎會從離它最近的作用域開始查找,也就是離它最近的變數對象(VO)開始查找,找不到就往上一層VO直到全域找到為止。